package com.baidu.iit.pxp.el.resolver;

import com.baidu.iit.pxp.el.ELContext;
import com.baidu.iit.pxp.el.PropertyNotWritableException;

import java.beans.FeatureDescriptor;
import java.util.Iterator;
import java.util.Map;

/**
 * User: huangweili
 * Date: 14-4-29
 * Time: 下午5:40
 */
public class MapELResolver extends ELResolver {

    private final boolean readOnly;


    public MapELResolver() {
        this(false);
    }


    public MapELResolver(boolean readOnly) {
        this.readOnly = readOnly;
    }

    @Override
    public Class<?> getCommonPropertyType(ELContext context, Object base) {
        return isResolvable(base) ? Object.class : null;
    }

    @Override
    public Iterator<FeatureDescriptor> getFeatureDescriptors(ELContext context, Object base) {
        if (isResolvable(base)) {
            Map<?, ?> map = (Map<?, ?>) base;
            final Iterator<?> keys = map.keySet().iterator();
            return new Iterator<FeatureDescriptor>() {
                public boolean hasNext() {
                    return keys.hasNext();
                }

                public FeatureDescriptor next() {
                    Object key = keys.next();
                    FeatureDescriptor feature = new FeatureDescriptor();
                    feature.setDisplayName(key == null ? "null" : key.toString());
                    feature.setName(feature.getDisplayName());
                    feature.setShortDescription("");
                    feature.setExpert(true);
                    feature.setHidden(false);
                    feature.setPreferred(true);
                    feature.setValue(TYPE, key == null ? null : key.getClass());
                    feature.setValue(RESOLVABLE_AT_DESIGN_TIME, true);
                    return feature;

                }

                public void remove() {
                    throw new UnsupportedOperationException("cannot remove");
                }
            };
        }
        return null;
    }


    @Override
    public Class<?> getType(ELContext context, Object base, Object property) {
        if (context == null) {
            throw new NullPointerException("context is null");
        }
        Class<?> result = null;
        if (isResolvable(base)) {
            result = Object.class;
            context.setPropertyResolved(true);
        }
        return result;
    }


    @Override
    public Object getValue(ELContext context, Object base, Object property) {
        if (context == null) {
            throw new NullPointerException("context is null");
        }
        Object result = null;
        if (isResolvable(base)) {
            result = ((Map<?, ?>) base).get(property);
            context.setPropertyResolved(true);
        }
        return result;
    }


    @Override
    public boolean isReadOnly(ELContext context, Object base, Object property) {
        if (context == null) {
            throw new NullPointerException("context is null");
        }
        if (isResolvable(base)) {
            context.setPropertyResolved(true);
        }
        return readOnly;
    }


    @Override
    @SuppressWarnings("unchecked")
    public void setValue(ELContext context, Object base, Object property, Object value) {
        if (context == null) {
            throw new NullPointerException("context is null");
        }
        if (isResolvable(base)) {
            if (readOnly) {
                throw new PropertyNotWritableException("resolver is read-only");
            }
            ((Map) base).put(property, value);
            context.setPropertyResolved(true);
        }
    }


    private final boolean isResolvable(Object base) {
        return base instanceof Map<?, ?>;
    }
}
